/*
*	Created:			15.06.10
*	Author:				009
*	Last Modifed:		-
*/

#include "os.h"
#include "main.h"

// server versions defines
#define SAMP_VERSION_034		1
#define SAMP_VERSION_0351		2
#define SAMP_VERSION_0352		3
#define SAMP_VERSION_036		4
#define SAMP_VERSION_037		5
#define SAMP_VERSION_038		6
// server address defines
#ifdef WIN32
	// R4
	#define R4_CALL_FROM_THREAD		0x46B666
	#define R4_CALL_NOP_THREAD		0x46B682
	#define R4_TICK_RATE			0x4A3FEC
	// R5-1
	#define R51_CALL_FROM_THREAD	0x479B66
	#define R51_CALL_NOP_THREAD		0x479B82
	#define R51_TICK_RATE			0x4B2084
	// R5-2
	#define R52_CALL_FROM_THREAD	0x45CE56
	#define R52_CALL_NOP_THREAD		0x45CE72
	#define R52_TICK_RATE			0x4A4734
	// R6
	#define R6_CALL_FROM_THREAD		0x4792B6
	#define R6_CALL_NOP_THREAD		0x4792D2
	#define R6_TICK_RATE			0x4ADDA4
	// R7
	#define R7_CALL_FROM_THREAD		0x479336
	#define R7_CALL_NOP_THREAD		0x479352
	#define R7_TICK_RATE			0x4ADDA4
	// R8
	#define R8_CALL_FROM_THREAD		0x479336
	#define R8_CALL_NOP_THREAD		0x479352
	#define R8_TICK_RATE			0x4ADFA4
#else
	// R4
	#define R4_CALL_FROM_THREAD		0x8074ED4
	#define R4_CALL_NOP_THREAD		0x8074EE6
	#define R4_TICK_RATE			0x81369C4
	// R5-1
	#define R51_CALL_FROM_THREAD	0x8076FBD
	#define R51_CALL_NOP_THREAD		0x8076FCF
	#define R51_TICK_RATE			0x813A9A4
	// R5-2
	#define R52_CALL_FROM_THREAD	0x0
	#define R52_CALL_NOP_THREAD		0x0
	#define R52_TICK_RATE			0x0
	// R6
	#define R6_CALL_FROM_THREAD		0x8076FBD
	#define R6_CALL_NOP_THREAD		0x8076FCF
	#define R6_TICK_RATE			0x813A9A4
	// R7
	#define R7_CALL_FROM_THREAD		0x8076FBD
	#define R7_CALL_NOP_THREAD		0x8076FCF
	#define R7_TICK_RATE			0x813A9A4
	// R8
	#define R8_CALL_FROM_THREAD		0x8076FBD
	#define R8_CALL_NOP_THREAD		0x8076FCF
	#define R8_TICK_RATE			0x813A9A4
#endif

#ifndef PAGESIZE
	#define PAGESIZE (4096)
#endif

MUTEX_IDENTIFY(MultiThreadMutex);
int TickRate = 5;

// call hook func
bool Unlock(void *address, int len)
{
	#ifdef WIN32
		DWORD
			oldp;
		// Shut up the warnings :D
		return !!VirtualProtect(address, len, PAGE_EXECUTE_READWRITE, &oldp);
	#else
		return !mprotect((void*)(((int)address / PAGESIZE) * PAGESIZE), PAGESIZE, PROT_WRITE | PROT_READ | PROT_EXEC);
	#endif
}

void CallHook(DWORD from, DWORD to) 
{
	if(Unlock((void*)from,5))
	{
		DWORD disp = to - (from + 5);
		*(BYTE *)(from) = 0xE8;
		*(DWORD *)(from + 1) = (DWORD)disp;
	}
}

void SAMP_ThreadComplete()
{
	// free mutex
	MUTEX_EXIT(MultiThreadMutex);
	// sleep
	SLEEP(TickRate);
	// close mutex
	MUTEX_ENTER(MultiThreadMutex);
}

// multithread init
void MultiThreadInit(int ServerVersion)
{
	// create mutex
	CREATE_MUTEX(MultiThreadMutex);
	// set hook
	switch(ServerVersion)
	{
	case SAMP_VERSION_034:
		{
			CallHook(R4_CALL_FROM_THREAD,(DWORD)SAMP_ThreadComplete);
			for(int i = R4_CALL_FROM_THREAD + 5;i < R4_CALL_NOP_THREAD;i++) *(BYTE*)(i) = 0x90;
			TickRate = *(DWORD*)(R4_TICK_RATE);
			break;
		}
	case SAMP_VERSION_0351:
		{
			CallHook(R51_CALL_FROM_THREAD,(DWORD)SAMP_ThreadComplete);
			for(int i = R51_CALL_FROM_THREAD + 5;i < R51_CALL_NOP_THREAD;i++) *(BYTE*)(i) = 0x90;
			TickRate = *(DWORD*)(R51_TICK_RATE);
			break;
		}
	case SAMP_VERSION_0352:
		{
			CallHook(R52_CALL_FROM_THREAD,(DWORD)SAMP_ThreadComplete);
			for(int i = R52_CALL_FROM_THREAD + 5;i < R52_CALL_NOP_THREAD;i++) *(BYTE*)(i) = 0x90;
			TickRate = *(DWORD*)(R52_TICK_RATE);
			break;
		}
	case SAMP_VERSION_036:
		{
			CallHook(R6_CALL_FROM_THREAD,(DWORD)SAMP_ThreadComplete);
			for(int i = R6_CALL_FROM_THREAD + 5;i < R6_CALL_NOP_THREAD;i++) *(BYTE*)(i) = 0x90;
			TickRate = *(DWORD*)(R6_TICK_RATE);
			break;
		}
	case SAMP_VERSION_037:
		{
			CallHook(R7_CALL_FROM_THREAD,(DWORD)SAMP_ThreadComplete);
			for(int i = R7_CALL_FROM_THREAD + 5;i < R7_CALL_NOP_THREAD;i++) *(BYTE*)(i) = 0x90;
			TickRate = *(DWORD*)(R7_TICK_RATE);
			break;
		}
	case SAMP_VERSION_038:
		{
			CallHook(R8_CALL_FROM_THREAD,(DWORD)SAMP_ThreadComplete);
			for(int i = R8_CALL_FROM_THREAD + 5;i < R8_CALL_NOP_THREAD;i++) *(BYTE*)(i) = 0x90;
			TickRate = *(DWORD*)(R8_TICK_RATE);
			break;
		}
	}
}
